#include "main.h"

/* function mul qui calcule la multiplication */
static int
mul(int depth)
{
	int i;
	switch (scanf("%d", &i)) {
		case EOF :
			return 1; /* neutral element */
			
		case 0 :
			return mul(depth+1); /* erroneous read */

		case 1 :
			/*if (i)
				return i * mul(depth+1);
			else
				return 0;*/
			return i * mul(depth+1);

	}
	return 0;
}


/**************************************************************

La partie creation du contexte

**************************************************************/

/*	int try(struct ctx_s *pctx, func_t *f, int arg)
*
*	int throw(struct ctx_s *pctx, int r)
*/

int init_ctx(struct ctx_s *ctx, int stack_size, func_t f, void *args)
{
	ctx->stack = malloc(stack_size);
	if(!ctx->stack) return 0;
	ctx->esp = (void *)((unsigned char*)ctx->stack + stack_size -4);
	ctx->ebp = (void *)((unsigned char*)ctx->stack + stack_size -4);
	ctx->f = f;
	ctx->args = args;
	ctx->etat = INITIAL;
	ctx->next_sem = NULL;
	return 0;
}

void switch_to_ctx(struct ctx_s *ctx)
{
	irq_disable();
	assert(ctx->etat!=FINI);
	if (current_ctx)
	__asm__("movl %%esp, %0" "\n\t" "movl %%ebp, %1"
	    :"=r"(current_ctx->esp),"=r"(current_ctx->ebp)   	
		);
	current_ctx=ctx;
    	__asm__("movl %0, %%esp" "\n" "movl %1, %%ebp"
        :
        :"r"(current_ctx->esp),"r"(current_ctx->ebp)
    	);
	irq_enable();
    if (current_ctx->etat == INITIAL)
    {
        current_ctx->etat=ACTIF;
    	current_ctx->f(current_ctx->args);  
    	current_ctx->etat=FINI;
    	free(current_ctx->stack);
    	exit(EXIT_SUCCESS); 
    }
}  

/**************************************************************

La partie changement du contexte

**************************************************************/


struct ctx_s ctx_ping;
struct ctx_s ctx_pong;

static int i;


void f_ping(void *arg);
void f_pong(void *arg);
/*int main(int argc, char *argv[])
{
	i = 0;
	init_ctx(&ctx_ping, 16384, f_ping, NULL);
	init_ctx(&ctx_pong, 16384, f_pong, NULL);
	switch_to_ctx(&ctx_ping);
	exit(EXIT_SUCCESS);
}*/
void f_ping(void *args)
{
	while(i<100) {
		printf("A") ;
		switch_to_ctx(&ctx_pong);
		i++;
		printf("B") ;
		switch_to_ctx(&ctx_pong);
		i++;
		printf("C") ;
		switch_to_ctx(&ctx_pong);
		i++;
	}
}
void f_pong(void *args)
{
	while(i<100) {
		printf("1") ;
		switch_to_ctx(&ctx_ping);
		i++;
		printf("2") ;
		switch_to_ctx(&ctx_ping);
		i++;
	}
}


/**************************************************************

La partie Ordonnancement

**************************************************************/


/* ce primitive est le changement de init_ctx, en effet c'est qu'on ajoute un attribut next pointe sur le prochain contexte pour que on puisse memoriser l'ensemble de contexte sous-forme d'une liste */
int create_ctx(int stack_size, func_t f, void *args)
{
	struct ctx_s *ctx = (struct ctx_s*)malloc(sizeof(struct ctx_s));
	if(!ctx) return 0;	

	init_ctx(ctx, stack_size, f, args);
	/* cas ou il n'y a pas de current_ctx ni de first_ctx, pas de contexte stocke*/
	if((!current_ctx) && (!first_ctx))
	{
		ctx->next = ctx;
		first_ctx = ctx;
	}
	/* cas ou il y a le current_ctx ,on ajoute ce contexte au suivant de current_ctx */
	else if(current_ctx)
	{
		ctx->next = current_ctx->next;
		current_ctx->next = ctx;
	}
	/* cas ou il y a de first_ctx, mais pas de current_ctx, on ajoute apres le first_ctx */
	else
	{
		ctx->next = first_ctx->next;
		first_ctx->next = ctx;
	}
	return 0;
}

/* ce primitive a pour l'objetif de passer d'un contexte au contexte suivant */
void yield(void)
{
	/* cas ou on passe la suite */
	if(current_ctx)
		switch_to_ctx(current_ctx->next);
	/* cas on n'a pas de contexte, passe au premier */
	else
		switch_to_ctx(first_ctx);
}

/**************************************************************

La partie Ordonnancement sur interruptions

**************************************************************/


void start_sched(){
	
	start_hw();

	setup_irq(TIMER_IRQ, yield);
	
	irq_enable();
	
}

/**************************************************************

La partie Synchronisation entre contextes

**************************************************************/

void sem_init(struct sem_s *sem, unsigned int val)
{
	sem->cpt = val;
	sem->ctx = (struct ctx_s*) NULL;
}

void sem_up(struct sem_s *sem)
{
	irq_disable();
	if(sem->ctx == NULL)
	{
		sem->cpt++;
	}
	else
	{
		sem->ctx = sem->ctx->next_sem;
		if(sem->ctx != NULL)
		{
		sem->ctx->etat = INITIAL;
		}
	}
	irq_enable();
}

void add_sem_ctx(struct sem_s * sem,struct ctx_s *ctx)
{
	struct ctx_s *ctx_parcours = (struct ctx_s*)malloc(sizeof(struct ctx_s));
	if(sem->ctx == NULL)
	{
		sem->ctx = ctx;
	}
	else
	{
		ctx_parcours = sem->ctx;
		while(ctx_parcours->next_sem != NULL)
		{
			ctx_parcours =ctx_parcours->next_sem; 
		}
		ctx_parcours->next_sem = ctx;
	}
	
}


void sem_down(struct sem_s *sem)
{
	irq_disable();
	if(sem->cpt > 0)
	{
		sem->cpt--;
		irq_enable();
		return;
	}
	current_ctx->etat = FINI;
	add_sem_ctx(sem,current_ctx);
	irq_enable();
}




/**********************
TEST*******************
*********************/

/* nombre de places dans le tampon */
#define N 100

struct sem_s mutex, vide, plein;

typedef struct objet_s
{
	int nombre; 
} objet_t;

void
produire_objet(objet_t *objet)
{
	objet->nombre+=50;
	printf("le nombre de produit est %d",objet->nombre);	
}

void
mettre_objet(objet_t *objet)
{
	objet->nombre++;
	printf("le nombre de produit est %d",objet->nombre);
}

void
retirer_objet (objet_t *objet)
{
	objet->nombre--;
	printf("le nombre de produit est %d",objet->nombre);
}

void
utiliser_objet(objet_t *objet)
{
	objet->nombre-=50;
	printf("le nombre de produit est %d",objet->nombre);
}


void producteur (void)
{
	objet_t objet;
	while (1) {
		produire_objet(&objet); /* produire l'objet suivant */
		sem_down(&vide); /* dec. nb places libres */
		sem_down(&mutex); /* entree en section critique */
		mettre_objet(&objet); /* mettre l'objet dans le tampon */
		sem_up(&mutex); /* sortie de section critique */
		sem_up(&plein); /* inc. nb place occupees */
	}
}
void consommateur (void)
{
	objet_t objet ;
	while (1) {
		sem_down(&plein); /* dec. nb emplacements occupes */
		sem_down(&mutex); /* entree section critique */
		retirer_objet (&objet); /* retire un objet du tampon */
		sem_up(&mutex); /* sortie de la section critique */
		sem_up(&vide); /* inc. nb emplacements libres */
		utiliser_objet(&objet); /* utiliser l'objet */
	}
}



/******** main() *************/
int
main()
{
/*
int product;


printf("A list of int, please\n");
product = mul(0);
printf("product = %d\n", product);
*/

sem_init(&mutex, 1); /* controle d'acces au tampon */
sem_init(&vide, N); /* nb de places libres */
sem_init(&plein, 0); /* nb de places occupees */

return 1;



}
